
/*
 * sndpipe: A UNIX pipes-based audio stream processor toolkit.
 * Copyright (C) 2011  Bradley Worley
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to:
 *
 *   Free Software Foundation, Inc.
 *   51 Franklin Street, Fifth Floor
 *   Boston, MA  02110-1301, USA.
 */

/* include the sndpipe header. */
#include "sndpipe.h"

/* define the option string used to parse command-line arguments. */
#define OPTS_OPSTRING "x:C:c:1:2:D:r:f:b:F:p:A:o:w:d:Rm:T:k:h"

/* declare the array of getopt option structures for parsing. */
static struct option long_options[] = {
  { OPTS_L_FUNCTION,  required_argument, 0, OPTS_S_FUNCTION },
  { OPTS_L_CHANNELS,  required_argument, 0, OPTS_S_CHANNELS },
  { OPTS_L_CHANNEL,   required_argument, 0, OPTS_S_CHANNEL },
  { OPTS_L_CHAN_ONE,  required_argument, 0, OPTS_S_CHAN_ONE },
  { OPTS_L_CHAN_TWO,  required_argument, 0, OPTS_S_CHAN_TWO },
  { OPTS_L_DEVICE,    required_argument, 0, OPTS_S_DEVICE },
  { OPTS_L_RATE,      required_argument, 0, OPTS_S_RATE },
  { OPTS_L_FORMAT,    required_argument, 0, OPTS_S_FORMAT },
  { OPTS_L_BUFFER,    required_argument, 0, OPTS_S_BUFFER },
  { OPTS_L_FREQUENCY, required_argument, 0, OPTS_S_FREQUENCY },
  { OPTS_L_PHASE,     required_argument, 0, OPTS_S_PHASE },
  { OPTS_L_AMPLITUDE, required_argument, 0, OPTS_S_AMPLITUDE },
  { OPTS_L_OFFSET,    required_argument, 0, OPTS_S_OFFSET },
  { OPTS_L_WAVE,      required_argument, 0, OPTS_S_WAVE },
  { OPTS_L_DUTY,      required_argument, 0, OPTS_S_DUTY },
  { OPTS_L_REMOVE,    no_argument,       0, OPTS_S_REMOVE },
  { OPTS_L_MODE,      required_argument, 0, OPTS_S_MODE },
  { OPTS_L_DURATION,  required_argument, 0, OPTS_S_DURATION },
  { OPTS_L_COEFF,     required_argument, 0, OPTS_S_COEFF },
  { OPTS_L_HELP,      no_argument,       0, OPTS_S_HELP },
  { 0, 0, 0, 0 }
};

/* declare the locally stored command-line arguments. */
char **opts_argv;
unsigned long opts_argc;

/* opts_set: stores an option argument at its char index in a local array.
 * @opt: the option char index.
 * @arg: the option argument.
 *
 * this function manages the opts_argc and opts_argv global variables,
 * reallocating memory when an out-of-bound option char index is passed,
 * and storing all passed arguments. it's a lazy way of storing arguments
 * for later.
 */
int opts_set (const char opt, const char *arg) {
  /* declare required variables. */
  unsigned long opti;

  /* get the integer cast of the option char. */
  opti = (unsigned long) opt;

  /* ensure we successfully cast to an integer. */
  if (!opti) {
    /* print a failure message and return a failure code. */
    warnf ("unable to cast option char index '%c' to integer", opt);
    return SP_FAIL;
  }

  /* check if we need to resize the arguments array. */
  if (opti >= opts_argc) {
    /* make just enough room. yes, this wastes a bit of memory. */
    opts_argc = opti + 1;

    /* reallocate memory for the option arguments array. */
    opts_argv = (char **) realloc (opts_argv, sizeof (char *) * opts_argc);

    /* ensure we allocated new memory. */
    if (!opts_argv) {
      /* print a failure message and return a failure code. */
      warnf ("unable to allocate option argument array memory");
      return SP_FAIL;
    }
  }

  /* allocate memory for the argument string. */
  opts_argv[opti] = (char *) malloc (sizeof (char) * (strlen (arg) + 2));

  /* ensure we allocated memory for the string. */
  if (!opts_argv[opti]) {
    /* print a failure message and return a failure code. */
    warnf ("unable to allocate option argument string memory");
    return SP_FAIL;
  }

  /* copy the argument string into the array. */
  strcpy (opts_argv[opti], arg);

  /* return a successful status code. */
  return SP_OK;
}

/* opts_init: initialize the argument arrays for later use.
 * @argc: the number of arguments passed to main().
 * @argv: the string array containing arguments from main().
 *
 * this function parses the string array handed to us from main(), which
 * was generated from the command-line arguments passed to the program.
 * argument values are stored later for use by opts_get_string(),
 * opts_get_int() and opts_get_float().
 */
int opts_init (int argc, char **argv) {
  /* declare required variables. */
  int idx, optidx;

  /* allocate an initial small pointer for the arguments string array. */
  opts_argv = (char **) malloc (sizeof (char *));
  opts_argv[0] = NULL;
  opts_argc = 1;

  /* loop until we don't have any more arguments to parse. */
  while (1) {
    /* initialize the option index to zero. */
    optidx = 0;

    /* parse a new option from the argv array. break when done or on error. */
    idx = getopt_long (argc, argv, OPTS_OPSTRING, long_options, &optidx);
    if (idx == -1) break;

    /* determine which option was passed to the program. */
    switch (idx) {
      /* required-argument options: store the argument. */
      case OPTS_S_FUNCTION:
      case OPTS_S_CHANNELS:
      case OPTS_S_CHANNEL:
      case OPTS_S_CHAN_ONE:
      case OPTS_S_CHAN_TWO:
      case OPTS_S_DEVICE:
      case OPTS_S_RATE:
      case OPTS_S_FORMAT:
      case OPTS_S_BUFFER:
      case OPTS_S_FREQUENCY:
      case OPTS_S_PHASE:
      case OPTS_S_AMPLITUDE:
      case OPTS_S_OFFSET:
      case OPTS_S_WAVE:
      case OPTS_S_DUTY:
      case OPTS_S_MODE:
      case OPTS_S_DURATION:
      case OPTS_S_COEFF:
        /* attempt to store the argument in the array. */
        if (opts_set (idx, optarg) != SP_OK) {
          /* print an error message and return failure. */
          warnf ("unable to store passed argument to '%c'", (char) idx);
          return SP_FAIL;
        }
        break;

      /* -R,--remove and -h,--help: non-argument options. */
      case OPTS_S_REMOVE:
      case OPTS_S_HELP:
        /* set a default numerically parseable argument. */
        if (opts_set (idx, "1") != SP_OK) {
          /* print an error message and return failure. */
          warnf ("unable to store default argument to '%c'", (char) idx);
          return SP_FAIL;
        }
        break;

      /* other non-acceptable options. (things not parsed by getopt()). */
      default:
        /* print an error message and return failure. */
        warnf ("unable to parse options");
        return SP_FAIL;
    }
  }

  /* return a success status code. */
  return SP_OK;
}

/* opts_get_string: pull a stored argument string from the array.
 * @opt: the option char index.
 *
 * this function reads opts_argv to return a stored argument string
 * for use in other functions.
 */
char *opts_get_string (const char opt) {
  /* declare required variables. */
  unsigned long opti = (unsigned long) opt;

  /* return the argument string, or NULL if its undefined. */
  return (opti && opti < opts_argc ? opts_argv[opti] : NULL);
}

/* opts_get_int: pull a stored argument integer from the array.
 * @opt: the option char index.
 *
 * this function translates the string returned by opts_get_string
 * into an integer for later use.
 */
long opts_get_int (const char opt) {
  /* declare required variables. */
  char *str = opts_get_string (opt);

  /* return the translated integer, or zero if its undefined. */
  return (str ? atol (str) : 0);
}

/* opts_get_float: pull a stored argument float from the array.
 * @opt: the option char index.
 *
 * this function translates the string returned by opts_get_string
 * into a double for later use.
 */
double opts_get_float (const char opt) {
  /* declare required variables. */
  char *str = opts_get_string (opt);

  /* return the translated double, or zero if its undefined. */
  return (str ? atof (str) : 0.0);
}

